package com.iot08.api.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.iot08.api.config.redis.RedisClusterService;
import com.iot08.api.service.supplement.KafkaService;
import com.iot08.common.constant.CacheEnum;
import com.iot08.common.constant.Const;
import com.iot08.common.constant.ConstKafka;
import com.iot08.common.entity.app.Device;
import com.iot08.common.entity.app.DeviceLocation;
import com.iot08.common.entity.app.DeviceModels;
import com.iot08.common.utils.DateUtil;
import com.iot08.common.utils.HttpReq;
import com.iot08.common.utils.response.ResponseMap;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.Header;
import org.apache.http.message.BasicHeader;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 对外开放接口相关
 * @author weichao 1299101858@qq.com
 * @version 1.0
 * @date 5/26/22 4:39 PM
 */
@Slf4j
@Service
public class OpenInterfaceService {

    @Value("${OpenInterface.KeyMapJSONStr}")
    private String openInterfacekeyMapJSONStr;

    @Resource
    private DeviceService deviceService;

    @Resource
    private DeviceLocationService deviceLocationService;

    @Resource
    private DeviceModelsService deviceModelsService;

    @Resource
    private RedisClusterService redisClusterService;

    @Resource
    private KafkaService kafkaService;


    /**
     * 对外开放的接口 根据IMEI号获取最近一次定位信息的接口
     * @param paramsMap
     * @param request
     * @time: 5/27/22 5:35 PM
     * @author: Weichao
     * @version 1.0
     *     error	0或者1	0为正常，1为错误
     *     msg	文字信息	返回状态描述
     *     例如：失败原因
     *     imei	15位的IMEI号	手机的IMEI号
     *     x	115.6666	经度
     *     y	35.66666	纬度
     *     addtime	10位时间戳	时间戳，整数，定位时间
     *     address 定位点的位置数据
     *     battery 电量
     */
    public ResponseMap getLastLoction(JSONObject paramsMap, HttpServletRequest request){
        ResponseMap res = checkFuntion(paramsMap, request, null);
        if(res.get("error").toString().equals("0")){
            Device device = deviceService.findById(paramsMap.getString("imei"));
            JSONObject deviceLastLocation = null;
            if(device.getLoc() != null){
               deviceLastLocation = new JSONObject(device.getLoc());
            }

            JSONObject nowloc = null;
            if(device.getNowloc() != null ){
                nowloc = new JSONObject(device.getNowloc());
            }

            if (nowloc == null && deviceLastLocation == null) {
                res.put("error", 1);
                res.put("msg", "暂无定位数据");
                return res;
            }
            if (nowloc == null) {
                try {
                    res.put("x", deviceLastLocation.get("lon"));
                    res.put("y", deviceLastLocation.get("lat"));
                    res.put("addtime", deviceLastLocation.get("t"));
                    res.put("address", deviceLastLocation.getOrDefault("address", ""));
                }catch (Exception error){
                    log.error("开发接口报错imei:"+ device.getId()+" error:"+error.getMessage(), error);
                    res.put("error", 1);
                    res.put("msg", "内部错误");
                    res.put("imei", device.getId());
                    return res;
                }
            }else{

                if (nowloc.getLongValue("timestamp") >= deviceLastLocation.getLongValue("t")) {
                    res.put("x", nowloc.get("lon"));
                    res.put("y", nowloc.get("lat"));
                    res.put("addtime", nowloc.get("timestamp"));
                    String add = nowloc.getOrDefault("fuzzyAdd", "").toString();
                    if(add.length() == 0){
                        add = nowloc.getString("address");
                        if(add.length() == 0){
                            add = "";
                        }
                    }
                    res.put("address", add);
                }else{
                    res.put("x", deviceLastLocation.get("lon"));
                    res.put("y", deviceLastLocation.get("lat"));
                    res.put("addtime", deviceLastLocation.get("t"));
                    res.put("address", deviceLastLocation.getOrDefault("address", ""));
                }
            }
            res.put("battery", device.getBattery());
            log.info("开放接口查询设备imei:{} 最后一次定位数据成功", device.getId());
        }



        return res;
    }


    /**
     *  对外开放的接口 根据IMEI号下发一次定位信息
     * @param paramsMap
     * @param request
     * @return
     *
     * error    0或者1	0为正常，1为错误
     *     msg	文字信息	返回状态描述
     *     例如：失败原因
     *     imei	15位的IMEI号	手机的IMEI号
     */
    public ResponseMap postCmdDevloc(JSONObject paramsMap, HttpServletRequest request){
        ResponseMap res = checkFuntion(paramsMap, request, null);
        if(res.get("error").toString().equals("0")){
            Device device = deviceService.findById(paramsMap.getString("imei"));
            log.info("根据IMEI号下发一次定位信息 imei:{} paramsMap:{}", device.getId(), paramsMap);
            // 判断是否操作频繁
            String vendorCode = request.getHeader("vendorCode");
            JSONObject openInterfacekeyMap = JSONObject.parseObject(openInterfacekeyMapJSONStr);
            String privateKey = openInterfacekeyMap.getString(vendorCode);
            String saveKey = String.format("requestLocation&%s&%s" ,device.getId(), privateKey);

            String redisRes = redisClusterService.get(saveKey);
            if (redisRes != null) {
                res.put("error", 1);
                res.put("msg", "设备操作频繁");
                res.put("imei", device.getId());
                return res;
            }

            // 下发一次主动定位
            kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_RequestLocation, null, privateKey);
            log.info("开放接口下发一次主动定位 imei:%s 成功", device.getId());
            redisClusterService.setex(saveKey, 30, privateKey);
            res.put("msg", "远程下发主动定位成功");
            res.put("imei", device.getId());
            log.info("开放接口下发一次主动定位 imei:{} 成功", device.getId());
        }

        return res;
    }

    /**
     * 对外开放的接口 根据IMEI号获取指定时间段位置信息的接口
     * @param paramsMap
     * @param request
     * @time: 6/2/22 5:46 PM
     * @author: Weichao
     * @version 1.0
     * error	0或者1	0为正常，1为错误
     * msg	文字信息	返回状态描述
     * 例如：失败原因
     * list_nums	数字	查询结果条数
     * list_items	多条记录罗列下方内容
     * imei	15位的IMEI号	手机的IMEI号
     * x	115.6666	经度
     * y	35.66666	纬度
     * addtime	10位时间戳	时间戳，整数，定位时间
     * address 定位点的位置数据
     */
    public ResponseMap getHistory(JSONObject paramsMap, HttpServletRequest request){
        ResponseMap res = checkFuntion(paramsMap, request, null);
        if(res.get("error").toString().equals("0")){
            String imei = paramsMap.getString("imei");
            Device device = deviceService.findById(imei);
            log.info("根据IMEI号获取指定时间段位置信息的接口 imei:{} paramsMap:{}", imei, paramsMap);
            long startTime = paramsMap.getLongValue("start_time");
            long endTime = paramsMap.getLongValue("end_time");

            Criteria criteria = Criteria.where("i").is(imei).andOperator(Criteria.where("t").gte(startTime), Criteria.where("t").lte(endTime));
            Query query = new Query(criteria);
            List<DeviceLocation> deviceLocations = deviceLocationService.find(query);
            JSONArray listItems = new JSONArray();
            if (deviceLocations != null && deviceLocations.size() > 0) {
                for (DeviceLocation item : deviceLocations) {
                    JSONObject tempItemOB = new JSONObject();
                    tempItemOB.put("id", item.getId());
                    tempItemOB.put("imei",imei);
                    tempItemOB.put("x", item.getLon());
                    tempItemOB.put("y", item.getLat());
                    tempItemOB.put("address", item.getAddr());
                    tempItemOB.put("addtime", item.getTime());
                    listItems.add(tempItemOB);
                }
            }
            if (listItems.size() == 0){
                res.put("error", 1);
                res.put("msg", "暂无轨迹数据");
                res.put("imei", imei);
                return res;
            }
            res.put("error", 0);
            res.put("list_nums", listItems.size());
            res.put("list_items", listItems);
            log.info("开放接口 查询设备imei: {} 定位数据成功 ", device.getId());
        }

        return res;
    }

    /**
     * 对外开放的接口 根据IMEI号 获取/添加/修改/删除 电子围栏接口
     * @param paramsMap
     * @param request
     * @time: 6/2/22 6:41 PM
     * @author: Weichao
     * @version 1.0
     *     error	0或者1	0为正常，1为错误
     *     msg	文字信息	返回状态描述
     *     例如：失败原因
     *     imei	15位的IMEI号	手机的IMEI号
     */
    public ResponseMap operationDevrail(JSONObject paramsMap, HttpServletRequest request){

        ResponseMap res = checkFuntion(paramsMap, request, null);
        if(res.get("error").toString().equals("0")){
            String imei = paramsMap.getString("imei");
            Device device = deviceService.findById(imei);
            res.put("imei", imei);
            //根据cmd 调用对应的方法
            String cmd = paramsMap.getString("cmd");
            if ("get".equalsIgnoreCase(cmd)) {
                log.info("查询设备安全区域成功 imei:{} paramsMap:{}", imei, paramsMap);
                res.put("msg", "查询电子围栏成功");
                JSONArray listRail = new JSONArray();
                Map<String, Map<String, Object>> railMap = device.getRailMap();
                for (String key : railMap.keySet()) {
                    JSONObject item = new JSONObject(railMap.get(key));
                    if (item.get("lon") != null) {
                        item.put("id", key);
                        item.put("updateTime", item.getLong("t"));
                        item.remove("t");
                        if (!item.containsKey("starttime")) {
                            item.put("starttime", "00:00");
                            item.put("endedtime", "24:00");
                            item.put("week", "1111111");
                            item.put("status", 1);
                        }
                    }
                    listRail.add(item);
                }


            }else if ("post".equalsIgnoreCase(cmd)) {

            }else if ("patch".equalsIgnoreCase(cmd)) {

            }else if ("delete".equalsIgnoreCase(cmd)) {

            }
            log.info("cmd:{} 设备安全区域 imei:{} 成功",cmd, device.getId());
        }
        return res;
    }

    /**
     * 对外开放的接口 根据IMEI号 获取/设置设备上传的定位间隔时间
     *
     *     error	0或者1	0为正常，1为错误
     *     msg	文字信息	返回状态描述
     *     locMode 定位模式  -1 0 1 2  (关闭/一小时/10分钟/3分钟)
     *     例如：失败原因
     *     imei	15位的IMEI号	手机的IMEI号
     * @param paramsMap
     * @param request
     * @time: 11/14/22 3:34 PM
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap getDevlocatetime(JSONObject paramsMap, HttpServletRequest request){

        ResponseMap res = checkFuntion(paramsMap, request, null);
        if(res.get("error").toString().equals("0")){
            String imei = paramsMap.getString("imei");
            Device device = deviceService.findById(imei);

            log.info("IMEI号 获取 设备上传的定位间隔时间 imei:{} paramsMap:{}", imei, paramsMap);
            Integer locMode = device.getLocMode();
            res.put("locMode", locMode);
            res.put("msg", "查询设备上传的定位间隔模式成功");
            log.info("获取 设备上传的定位间隔时间 imei:{} 成功", device.getId());
        }

        return res;
    }

    public ResponseMap postDevlocatetime(JSONObject paramsMap, HttpServletRequest request){
        ResponseMap res = checkFuntion(paramsMap, request, null);

        if(res.get("error").toString().equals("0")){
            String imei = paramsMap.getString("imei");
            Device device = deviceService.findById(imei);
            log.info("IMEI号 设置 设备上传的定位间隔时间 imei:{} paramsMap:{}", imei, paramsMap);
            if(!paramsMap.containsKey("locMode")){
                res.put("error", 1);
                res.put("msg", "miss key locMode");
                return res;
            }
            Integer second = 0;
            Integer locMode = paramsMap.getInteger("locMode");
            switch (locMode){
                case -1:
                    second = 600 * 60*24*365*2;
                    break;
                case 0:
                    second = 3600;
                    break;
                case 1:
                    second = 600;
                    break;
                case 2:
                    second = 180;
                    break;
                default:
                    return errorCodeGetRes(400, imei);
            }
            //下发 设置设备定位间隔
            JSONObject kafkaJson = new JSONObject();
            kafkaJson.put("second",second);
            String vendorCode = request.getHeader("vendorCode");
            kafkaService.emitDeviceDemand(imei,ConstKafka.DEVICE_TYPE_LocationUploadInterval, kafkaJson, "OpenInter&"+vendorCode);
            //更新一下数据库
            Update updateDevice = new Update();
            updateDevice.set("locMode", locMode);
            deviceService.updateById(imei, updateDevice);


            res.put("msg", "设置设备上传的定位间隔模式成功");
            log.info("imei:{} 厂家:{}后台调用 设备设置 定位时间间隔 locMode:{} 成功" ,imei, vendorCode, locMode);
        }

        return res;
    }



    /**
     * 统一检查方法
     * @param paramsMap
     * @param request
     * @param device
     * @return
     */
    private ResponseMap checkFuntion(JSONObject paramsMap, HttpServletRequest request, Device device){
        String imei = paramsMap.getString("imei");
        int code = checkImei(paramsMap, imei);
        if (code != 0) {
            return errorCodeGetRes(code, imei);
        }
        int code2 = checkParams(paramsMap, request, device);
        if (code2 != 0) {
            return errorCodeGetRes(code2, imei);
        }
        ResponseMap res = new ResponseMap();
        res.put("error", 0);
        res.put("msg", "");
        res.put("imei", imei);
        return res;
    }

    /**
     * 检查Imei 号
     * @param paramsMap
     * @param imei
     * @time: 5/27/22 5:34 PM
     * @author: Weichao
     * @version 1.0
     */
    private int checkImei(JSONObject paramsMap, String imei){
        if(!paramsMap.containsKey("imei")) {
            return -100; //无imei号
        }
        imei = paramsMap.getString("imei");

        return 0;
    }
    /**
     * 参数检查, 返回检查结果码
     * @param paramsMap
     * @param request
     * @time: 5/27/22 4:07 PM
     * @author: Weichao
     * @version 1.0
     */
    private int checkParams(JSONObject paramsMap, HttpServletRequest request, Device device){
        if( paramsMap.containsKey("imei")) {
            String imei = paramsMap.getString("imei");
            String vendorCode = request.getHeader("vendorCode");
            if (vendorCode == null) {
                return -200;
            }
            String deviceType = request.getHeader("deviceType");
            if (deviceType == null) {
                return -700;
            }
            JSONObject openInterfacekeyMap = JSONObject.parseObject(openInterfacekeyMapJSONStr);
            if(openInterfacekeyMap.get(vendorCode) == null){
                return -200;
            }
            String authKey = request.getHeader("authKey");
            if (authKey == null) {
                return -400;
            }
            //检查认证Key
            String privateKey = openInterfacekeyMap.getString(vendorCode);
            Long timestamp = paramsMap.getLong("timestamp");
            String tempStr = String.format("%s&%s",privateKey, timestamp);
            String MD5key = DigestUtils.md5Hex(tempStr).toLowerCase();
            if (!authKey.equalsIgnoreCase(MD5key)) {
                return -300;
            }
            //检查设备型号
            Query query = new Query();
            query.addCriteria(Criteria.where("vender").is(vendorCode).and("model").is(deviceType));
            DeviceModels dModel = deviceModelsService.findOne(query);
            if (dModel == null) {
                return -800;
            }
            device = deviceService.findById(imei);
            if (device == null) {
                return -500;
            }
            if (!device.getModelId().equals(dModel.getId())) {
                //型号不对
                return -600;
            }


        }else{
            return -100; //无imei号
        }
        return 0;
    }

    /**
     * 通过错误码返回内容
     * @param code
     * @param imei
     * @time: 5/27/22 4:52 PM
     * @author: Weichao
     * @version 1.0
     */
    private ResponseMap errorCodeGetRes(int code, String imei){
        ResponseMap res = new ResponseMap();
        res.put("error", 1);
        res.put("imei", imei);
        String msg = "";
        switch (code) {
            case -100: {
                msg = "miss key imei";
            }
            break;
            case -200: {
                msg = "miss headerkey vendorCode";
            }
            break;
            case -700: {
                msg = "miss headkey deviceType";
            }
            break;
            case -300: {
                msg = "认证失败.Key值错误";
            }
            break;
            case -400: {
                msg = "参数错误";
            }
            break;
            case -500: {
                msg = "查无此imei";
            }
            break;
            case -600: {
                msg = "imei型号不正确";
            }
            break;
            case -800: {
                msg = "not find imei with deviceType";
            }
            break;


            default:
                msg = "未知错误 code:"+code;
                break;
        }
        res.put("msg", msg);
        return res;

    }

    /**
     * 向外回调告警信息
     * @param imei
     * @param vendorCode
     * @param msgType
     * @param railId
     * @param railName
     *     200：出围栏
     *     201：入围栏
     *     301：SOS
     *     501：电量低告警
     */
    public void pushAlarmCallOutInterface(String imei, String vendorCode, int msgType, String railId, String railName){
        try {
            String url = "http://36.133.144.67:19088/tsmReceive/tx/receivedata";
            log.info("imei:{} 向外回调告警信息msgType:{} 请求开始" , imei, msgType);
            String KEY = "URYDGS26JUY6A1";
            String APPSECRET = "tx8b1f47743c6941c8bfe763dca54bf4b9";
            String nowTime = DateUtil.nowTimestampToLong().toString();
            String tempStr = String.format("%s%s%s", KEY, nowTime, APPSECRET);
            String MD5key = DigestUtils.md5Hex(tempStr).toLowerCase();
            Header[] headers = {
                    new BasicHeader("Content-type", "application/x-www-form-urlencoded")
                    ,new BasicHeader("Authorization", MD5key)
            };
            Map<String, String> param = new HashMap<>();
            param.put("imei", imei);
            param.put("timestamp", nowTime);
            param.put("msgType", ""+msgType);
            if(msgType == 200 || msgType == 201){
                param.put("railId", railId);
                param.put("railName", railName);
            }
            String msg = String.format("imei:%s 赛诺后台回调vendorCode:%s 告警接口, msgType:%s", imei, vendorCode, msgType);
            param.put("msg", msg);

            String res = HttpReq.sendPostParam(url, param, headers);
            log.info("imei:{} 向外回调告警信息msgType:{} 请求完成 结果res:{}", imei, msgType, res);

        } catch (Exception error){
            log.error(String.format("imei:%s 向外回调告警信息msgType:%s 请求错误 %s error" ,imei, msgType, error.getMessage()), error);
        }

    }


}
